/*# Capture ESXi system information.
#
# Written by: Alaa Hleihel <alaa@mellanox.com>
*/

#include "methods.h"
#include "CommandList.h"
#include "vars.h"

#include <sys/wait.h>
#include <iostream>
#include <sys/types.h>
#include <fcntl.h>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fstream>
#include <vector>
#include <cstring>
#include <sys/types.h> 

using namespace std;

//----------------------------------------------------------------------------------------
// 
// runs a command in a new process, and returns:
//		the pid of the process
//  	the stdin stdout stderr handlers for the command's process
pid_t execCommand(char **command, int *infp, int *outfp, int *errfp)
{
	if (!command)
	{
		return FAIL;
	}
    // process pid
	pid_t pid;
	// 
    if (pipe(infp) != 0 || pipe(outfp) != 0 || pipe(errfp) != 0)
	{
        return -1;
	}
	// fork
    pid = fork();
	// check if fork failded
    if (pid < 0)
	{
        return pid;
	}
    else 
	{
		// is this the child process?
		if (pid == 0)
		{
			/* redirect the output */
			if (dup2(outfp[WRITE], STDOUT_FILENO) == -1)
				sys_err("STDOUT_FILENO");
		
			/* redirect the input */		
			if (dup2(infp[READ], STDIN_FILENO) == -1)
				sys_err("STDIN_FILENO");

			/* redirect the errors */		
			if (dup2(errfp[WRITE], STDERR_FILENO) == -1)
				sys_err("STDERR_FILENO");
			
			if ((close(outfp[READ])) == -1)
				sys_err("close");
			if ((close(infp[WRITE])) == -1)
				sys_err("close");
			if ((close(errfp[READ])) == -1)
				sys_err("close");
						
			// run the command
			execvp(*command, command);
			// if everything is ok, it shouldn't reach this point 
			// print error if any
			perror("execvp");
			// exit 
			exit(1);
			// return process pid
			return pid;
		}
	}
}
// #### end of execCommand
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
// format: char *command[] = {"ls", "-l", NULL};
string runCommand(char **command)
{
	CommandList cmdList = splitCommandByPipe(commandToString(command));
	string ret = "";
	string tmpOutput = "";
	for (int index = 0 ; index < cmdList.count ; index++)
	{
		ret = "";
		// stdin stdout handlers for the command process
		//int infp, outfp, errfp;
		int infp[2], outfp[2], errfp[2];
		pid_t childPID = -1;
		
		// run the first command, and get the handlers
		//cout << "**************: " << commandToString(cmdList.getAtIndex(index)).c_str() << endl;
		if (childPID  = execCommand(cmdList.getAtIndex(index), infp, outfp, errfp) <= 0)
		{	// error
			return "ERROR: Unable to exec command:\n";
		}
	
		// ******************************
		// get input stream
		FILE *stream, *st;
		if ((stream = fdopen (infp[WRITE], "w")) == NULL)
		{
			sys_err("fdopen");
		}
		if ((close(infp[READ])) == -1)
		{
			sys_err("close");
		}
		//++++++++++++++++++++
		// pass the last output to the current command 
		for (int jj = 0 ; jj < tmpOutput.size() ; jj++)
		{
			fprintf(stream, "%c", tmpOutput[jj]);
			//cout << tmpOutput[jj];
		}
		
		//++++++++++++++++++++
		//close input side
		fflush(stream);
		if (close(infp[WRITE]) == -1)
		{
			sys_err("close1");
		}   
		// ******************************
		
		// ******************************
		// get output stream
		if ((st = fdopen (outfp[READ], "r")) == NULL)
		{
			sys_err("fdopen");
		}
		if ((close(outfp[WRITE])) == -1)
		{
			sys_err("close");
		}
		int c=1;
		char ch;
		// read the output		
		while( c > 0)
		{
			c = fscanf(st, "%c", &ch);
			if (c > 0)
			{
				ret.push_back(ch);
			}
		}
		tmpOutput = ret;
		//cout << ret.c_str() << endl;
		// ******************************
		
		// ******************************
		// read the errors
		string errors = "";
		if ((st = fdopen (errfp[READ], "r")) == NULL)
		{
			sys_err("fdopen");
		}
		if ((close(errfp[WRITE])) == -1)
		{
			sys_err("close");
		}
		c=1; 
		while( c > 0)
		{
			c = fscanf(st, "%c", &ch);
			if (c > 0)
				errors.push_back(ch);
		} 
		//cout << errors.c_str() << endl;
		// ******************************
		if (errors != "")
		{
			// add the errors to the output
			ret.append("in: ");
			ret.append(commandToString(cmdList.getAtIndex(index)));
			ret.append(errors); 
			// stop the loop
			break; 
		}	
	} // end of for
	
	// return the output
	return ret;
}
// #### end of runCommand
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
CommandList splitCommandByPipe(string cmd)
{
	CommandList ret;	
	string tmp;
	int inRegEx = 0;
	for (int ii = 0; ii < cmd.size() ; ii++)
	{
		if (cmd[ii] == ' ' && tmp.empty())
		{
			// do nothing
		}
		else
		{
			// check if we have regular expression, 
			// because the pipe '|' can be part of a regular expression....
			if (cmd[ii] == '\'')
			{
				inRegEx = (inRegEx + 1) % 2;
			}
			// check for pipe that is not part of a regular expression
			if (inRegEx == 0 && cmd[ii] == '|')
			{
				// remove last space " "
				if (tmp[tmp.size() - 1] == ' ')
				{
					tmp.resize(tmp.size() - 1);
				}
				// add the current command
				ret.add(tmp);
				// clear the temp string;
				tmp = "";
			}
			else
			{
				tmp.push_back(cmd[ii]);
			}
		}
	}
	if (tmp[tmp.size() - 1] == ' ')
	{
		tmp.resize(tmp.size() - 1);
	}
	// add the last command 
	ret.add(tmp);
	// return the command list 
	return ret;
}
// #### end of splitCommandByPipe
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
void sys_err(string s)
{
	fprintf(stderr, "\nerror at: %s\n", s.c_str());
	exit(1);
}
// #### end of sys_err
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
int initHtml(ofstream& file)
{
	if (file.is_open())
	{
		file << "<html><head></head><body><pre>" << endl;
		file << "<a name=\"index\"></a><h1>Mellanox Technologies</h1>" << endl;
		file << "<h2>ESXi and OFED System Information Snapshot Utility</h2>";
		file << "<h2>Version: " << VERSION << "</h2>" << endl;
		file << "<hr>" << endl;
	}
	else
	{
		return FAIL;
	}
}
// #### end of initHtml
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
int finalizeHtml(ofstream & file)
{
	if (file.is_open())
	{
		file << "</pre></body></html>" << endl;
	}
	else
	{
		return FAIL;
	}
}
// #### end of finalizeHtml
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
vector <string> strSplit(string str, char delimiter)
{
	vector <string> ret;
	if (str.empty() || delimiter < 0)
	{
		return ret;
	}
	string tmp = "";
	// loop over all characters in the string
	for (int i = 0; i < str.size() ; i++)
	{
		// is this the delimiter?
		if(str[i] == delimiter)
		{
			// the current string
			ret.push_back(tmp);			
			// clear the temp string
			tmp = "";
		}
		else
		{	
			// append to the current string
			tmp.push_back(str[i]);
		}
	}
	// add the last one.
	ret.push_back(tmp);
	// return the vector of strings
	return ret;
}
// #### end of 
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
char *strToCharArr(const std::string & str)
{
   char *ret = new char[str.size()+1]; // need to delete it later !!
   ret[str.size()] = 0;
   memcpy(ret, str.c_str(), str.size());
   return ret; 
}
// #### end of 
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
string commandToString(char ** cmdWithArgs)
{
	stringstream strStream;
	//
	for (int ii =0 ; cmdWithArgs[ii] != 0 ; ii++)
	{
		string element = cmdWithArgs[ii];
		if (ii > 0)
		{
			strStream << " ";
		}
		strStream << element;
	}
	//
	return strStream.str();
}
// #### end of commandToString
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
string strReplace(string str, char a, char b)
{
	string ret = ""; 
	for (int ii = 0 ; ii < str.size() ; ii++)
	{
		if (str[ii] == a)
		{
			ret.push_back(b);
		}
		else
		{
			ret.push_back(str[ii]);
		}
	}
	return ret;
}
// #### end of strReplace
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
void addIndexTable(ofstream & file, string title, CommandList cmdList)
{
	file << "<h2>" << title.c_str() << ":</h2>";
	file << "<table cols=\"4\" width=\"100\%\" border=\"0\" bgcolor=\"#E0E0FF\"><tr>";
	for (int ii = 0, col = 0; ii < cmdList.count ; ii++)
	{
		string cmd1 = commandToString(cmdList.getAtIndex(ii));
		file << "<td width=\"25\%\"><a href=\"#" << strReplace(strReplace(cmd1, ' ', '_'), '/', '_').c_str() << "\">" << cmd1.c_str() << "</a></td>";
		col++;
		if (col == COLS_NUM)
		{
			file << "</tr><tr>";
			col = 0;
		}
	}
	file << "</tr></table>";
}
// #### end of addIndexTable
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
void addElement(ofstream & file, string title, string content, string prev, string next)
{
	file << "<div>";
	// link to this element
	file << "<a name=\"" << strReplace(strReplace(title, ' ', '_'), '/', '_').c_str() << "\"></a>"; 
	// links to prev, index, next
	file << "<a href=\"#" << strReplace(strReplace(prev, ' ', '_'), '/', '_') << "\">[<-- Previous ]</a>   <a href=\"#index\">[ Back to index ]</a>   <a href=\"#" << strReplace(strReplace(next, ' ', '_'), '/', '_') << "\">[ Next -->]</a>";
	// title
	file << "<h2><u>" << title.c_str() << "</u></h2>"; 
	// output
	file << content.c_str();
	file << "</div>" << endl;
}
// #### end of addCommand
//----------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------
void addCommandList(ofstream & file, CommandList cmdList)
{
	string strEmpty = "";
	for (int ii = 0; ii < cmdList.count ; ii++)
	{	
		string cmd1 = commandToString(cmdList.getAtIndex(ii));
		//
		
		printf("%c[2K", 27); printf("Processing: %s\r", cmd1.c_str()); fflush(stdout);
		// first item
		if (ii == 0)
		{
			if (cmdList.count == 1)
			{
				addElement(file, cmd1,  runCommand(cmdList.getAtIndex(ii)), strEmpty, strEmpty);
			}
			else
			{
				addElement(file, cmd1,  runCommand(cmdList.getAtIndex(ii)), strEmpty, commandToString(cmdList.getAtIndex(ii+1)));
			}
		}
		else // last item
		if (ii == cmdList.count - 1)
		{
			addElement(file, cmd1,  runCommand(cmdList.getAtIndex(ii)), commandToString(cmdList.getAtIndex(ii-1)), strEmpty);
		}
		else 
		{
			addElement(file, cmd1,  runCommand(cmdList.getAtIndex(ii)), commandToString(cmdList.getAtIndex(ii-1)), commandToString(cmdList.getAtIndex(ii+1)));
		}
	}
}
// #### end of addCommandList
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
string readFile(string path)
{
	string line;
	stringstream ret;
	ifstream src;
	src.open(path.c_str());
	if (src.is_open())
	{
		while (!src.eof())
		{
			getline (src,line);
			ret << line << endl;
		}
		src.close();
	}
	else
	{
		return "ERROR: Unable to open file: " + path;
	}	
	return ret.str();
}
// #### end of readFile
//----------------------------------------------------------------------------------------


//----------------------------------------------------------------------------------------
void addFileList(ofstream & file, CommandList fileList)
{
	string strEmpty = "";
	for (int ii = 0; ii < fileList.count ; ii++)
	{	
		string filePath = commandToString(fileList.getAtIndex(ii));
		//
		printf("%c[2K", 27); printf("Loading: %s\r", filePath.c_str()); fflush(stdout);
		// first item
		if (ii == 0)
		{
			if (fileList.count == 1)
			{
				addElement(file, filePath,  readFile(filePath), strEmpty, strEmpty);
			}
			else
			{
				addElement(file, filePath,  readFile(filePath), strEmpty, commandToString(fileList.getAtIndex(ii+1)));
			}
		}
		else // last item
		if (ii == fileList.count - 1)
		{
			addElement(file, filePath,  readFile(filePath), commandToString(fileList.getAtIndex(ii-1)), strEmpty);
		}
		else 
		{
			addElement(file, filePath,  readFile(filePath), commandToString(fileList.getAtIndex(ii-1)), commandToString(fileList.getAtIndex(ii+1)));
		}
	}
}
// #### end of addFileList
//----------------------------------------------------------------------------------------
